/*
 * Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific path governing permissions and
 * limitations under the License.
 */
#include "application_context.h"
#include "bundle_info.h"
#include "bundle_mgr_interface.h"
#include "iservice_registry.h"
#include "vector"
#include <mutex>
#include "file_share_config.h"
#include "sandbox_helper.h"
#include "accesstoken_kit.h"
#include "ipc_skeleton.h"
#include "uri.h"
#include "send_manager.h"
#include <sys/stat.h>

namespace OHOS {
namespace FileShare {
const char *FILESHARE_PREFERENCES_FILE_NAME = "/Share";
std::string FILE_SHARE_PATH = "/data/service/el2/public/fileshare/";
const char *DEFAULT_PATH = "file://docs/storage/Users/currentUser/Share";
const char *WORK_DURATION = "workDuration";
const char *STORAGE_PATH = "storagePath";
const int DEFAULT_WORK_DURATION = 10;
FileShareConfig &FileShareConfig::GetInstance(){
    static FileShareConfig instance;
    return instance;
}

bool FileShareConfig::IsExistDir(const char *path)
{
    if (path == nullptr) {
        return false;
    }

    struct stat buf = {};
    if (stat(path, &buf) != 0) {
        return false;
    }
    return S_ISDIR(buf.st_mode);
}

bool FileShareConfig::MkOwnerDir(const char *dir)
{
    if (dir == nullptr) {
        return false;
    }
    if (IsExistDir(dir)) {
        LOGI("path already exist! %{public}s",dir);
        return true;
    }
    size_t len = strlen(dir);
    if (len == 0 || len > PATH_MAX) {
        return false;
    }
    // Create directories level by level
    char rootDir[PATH_MAX] = { '\0' };
    for (size_t i = 0; i < len; ++i) {
        rootDir[i] = dir[i];
        if ((rootDir[i] == '/' || i == (len - 1)) && !IsExistDir(rootDir)) {
            mode_t mode = S_IRWXU | S_IRWXG;
            if (mkdir(rootDir, mode) < 0) {
                return false;
            }
        }
    }
    return true;
}

std::shared_ptr<NativePreferences::Preferences> FileShareConfig::GetFileSharePreferences()
{
    Security::AccessToken::HapTokenInfo hapTokenInfo;
    int ret = Security::AccessToken::AccessTokenKit::GetHapTokenInfo(IPCSkeleton::GetCallingTokenID(), hapTokenInfo);
    if (ret != ERR_OK) {
        LOGE("GetHapTokenInfo error code: %{public}d", ret);
        return nullptr;
    }
    std::string fileSharePath = FILE_SHARE_PATH ;
    LOGD("fileShareDir: %{public}s", fileSharePath.c_str());
    if(!MkOwnerDir(fileSharePath.c_str())){
        LOGE("MkOwnerDir FILE_SHARE_PATH error");
        return nullptr;
    }
    std::string fileName = fileSharePath + "fileshareProperties.xml";
    int32_t errCode = NativePreferences::E_ERROR;
    std::shared_ptr<NativePreferences::Preferences> preferences = NativePreferences::PreferencesHelper::GetPreferences(fileName, errCode);
    if (preferences == nullptr || errCode != NativePreferences::E_OK) {
        LOGE("NativePreferences::PreferencesHelper::GetPreferences failed.");
        return nullptr;
    }
    LOGD("NativePreferences::PreferencesHelper::GetPreferences success");
    return preferences;
}

std::string FileShareConfig::GetStoragePath()
{
    std::shared_ptr<NativePreferences::Preferences> preferences = GetFileSharePreferences();
    if (preferences == nullptr) {
        LOGW("FileShareConfig::GetStoragePathFileShareConfig get fileshare preferences failed, return default path.");
        return DEFAULT_PATH;
    }
    std::string path = preferences->GetString(STORAGE_PATH, "");
    LOGD("GetStoragePath path %{public}s",path.c_str());
    if (path.length() == 0 || path.compare("default") == 0) {
        return DEFAULT_PATH;
    }
    LOGD("GetStoragePath path success %{public}s",path.c_str());
    return path;
}

int32_t FileShareConfig::SetStoragePath(std::string &path)
{
    LOGD("path  %{public}s",path.c_str());
    std::shared_ptr<NativePreferences::Preferences> preferences = GetFileSharePreferences();
    std::string realPath = path;
    if (path.compare("default") == 0) {
        path = DEFAULT_PATH;
    }

    if (path.find("://") != std::string::npos) {
        Uri uri(path);
        std::string sandboxPath = AppFileService::SandboxHelper::Decode(uri.GetPath());
        if (!AppFileService::SandboxHelper::IsValidPath(sandboxPath) || uri.GetScheme() != "file") {
            LOGE("Sandbox path from uri is error");
            return FS_STORAGE_URI_INVALID;
        }
        Security::AccessToken::HapTokenInfo hapTokenInfo;
        int ret = Security::AccessToken::AccessTokenKit::GetHapTokenInfo(IPCSkeleton::GetCallingTokenID(), hapTokenInfo);
        if (ret != ERR_OK) {
            LOGE("GetHapTokenInfo error code: %{public}d", ret);
            return FS_STORAGE_GET_HAPINFO_INVALID;
        }
        ret = AppFileService::SandboxHelper::GetPhysicalPath(path, std::to_string(hapTokenInfo.userID), realPath);
        if (ret != ERR_OK) {
            LOGE("Sandbox path getPhysicalPath error code: %{public}d", ret);
            return FS_STORAGE_URI_GET_PATH_FAILED;
        }
    }

    if (access(realPath.c_str(), F_OK) != 0) {
        LOGE("path can not access! %{public}s", realPath.c_str());
        return FS_STORAGE_PATH_ACCESS_FAILED;
    }
    
    if (preferences == nullptr) {
        LOGE("FileShareConfig::SetStoragePath get preferences nullptr.");
        return FS_SET_STORAGE_PATH_ERROR;
    }
    int32_t status = preferences->PutString(STORAGE_PATH, path);
    if (status != 0) {
        LOGE("FileShareConfig::SetStoragePath set preferences failed.");
        return FS_SET_STORAGE_PATH_ERROR;
    }
    preferences->Flush();
    LOGD("SetStoragePath path success %{public}s",path.c_str());
    return FS_SUCCESS;
}

int32_t FileShareConfig::GetWorkDuration()
{
    std::shared_ptr<NativePreferences::Preferences> preferences = GetFileSharePreferences();
    if (preferences == nullptr) {
        LOGW("FileShareConfig::GetStoragePathFileShareConfig get fileshare preferences failed, return default path.");
        return DEFAULT_WORK_DURATION;
    }
    int time = preferences->GetInt(WORK_DURATION, DEFAULT_WORK_DURATION);
    LOGD("GetWorkDuration time %{public}d",time);
    if (time == 0) {
        LOGD("0 means the share service will not close automatically");
    }
    LOGD("GetWorkDuration time success %{public}d",time);
    return (int32_t)time;
}

int32_t FileShareConfig::SetWorkDuration(int32_t &time)
{
    LOGD("time %{public}d",time);
    std::shared_ptr<NativePreferences::Preferences> preferences = GetFileSharePreferences();
    if (preferences == nullptr) {
        LOGE("FileShareConfig::SetStoragePath get preferences nullptr.");
        return FS_SET_WORK_DURATION_ERROR;
    }
    int32_t status = preferences->PutInt(WORK_DURATION, time);
    if (status != 0) {
        LOGE("FileShareConfig::SetStoragePath set preferences failed.");
        return FS_SET_WORK_DURATION_ERROR;
    }
    preferences->Flush();
    
    if(time == 0){
        StopTimer();
        return FS_SUCCESS;
    }

    bool shareStatus;
    SendManager::GetInstance().GetShareStatus(shareStatus);
    if(shareStatus){
        StartTimer();
    }
    LOGI("SetWorkDuration time success %{public}d",time);
    return FS_SUCCESS;
}

void FileShareConfig::RegisterCallback(std::function<void(void)> func){
    callbackFun_ = func;
}

void FileShareConfig::CountdownTimer(uint32_t totalSeconds) {
    while (totalSeconds > 0 && !cancelFlag_.load()) {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        --totalSeconds;
        LOGD("countdown time %{public}d",totalSeconds);
    }
    if (!cancelFlag_.load()) {
        callbackFun_();
        LOGI("countdown time finished");
    } else {
        LOGI("countdown time cancel");
    }
    isCountingDown_.store(false);
}

void FileShareConfig::StartTimer() {
    if (isCountingDown_.load()) {
        StopTimer();
    }
    cancelFlag_.store(false);
    isCountingDown_.store(true);
    if (timerThread_.joinable()) {
        timerThread_.join();
    }
    uint32_t countdownTotalSeconds = 60 * GetWorkDuration();
    timerThread_ = std::thread([countdownTotalSeconds,this]() {
        CountdownTimer(countdownTotalSeconds);
    });
    LOGI("start countdown time");
}

void FileShareConfig::StopTimer() {
    if (isCountingDown_.load()) {
        cancelFlag_.store(true);
        if (timerThread_.joinable()) {
            timerThread_.join();
        }
        LOGI("cancel countdown time");
        isCountingDown_.store(false);
    }
    LOGI("countdown time already cancel!");
}
} // namespace FileShare
} // namespace OHOS